package com.leonzhangxf.spel.reference;

import com.leonzhangxf.spel.reference.bean.Inventor;
import com.leonzhangxf.spel.reference.bean.PlaceOfBirth;
import com.leonzhangxf.spel.reference.bean.Society;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Collection Projection
 * <p>
 * Projection lets a collection drive the evaluation of a sub-expression,
 * and the result is a new collection.
 * <p>
 * The syntax for projection is .![projectionExpression].
 *
 * @author leonzhangxf
 * @date 20190427
 */
public class CollectionProjection {

    public static void main(String[] args) {
        ExpressionParser parser = new SpelExpressionParser();
        StandardEvaluationContext context = new StandardEvaluationContext();

        Society society = new Society();
        Inventor inventor = new Inventor("AAA", "Serbian");
        inventor.setPlaceOfBirth(new PlaceOfBirth("Smiljan", "China"));
        society.getMembers().add(inventor);

        Inventor inventor1 = new Inventor("BBB", "Serbian");
        inventor1.setPlaceOfBirth(new PlaceOfBirth("Idvor", "China"));
        society.getMembers().add(inventor1);

        Inventor inventor2 = new Inventor("CCC", "Serbian");
        inventor2.setPlaceOfBirth(new PlaceOfBirth("Smiljan", "America"));
        society.getMembers().add(inventor2);

        context.setRootObject(society);

        // For example, suppose we have a list of inventors but want the list of cities where they were born.
        // Effectively, we want to evaluate 'placeOfBirth.city' for every entry in the inventor list.
        // The following example uses projection to do so:

        // returns [Smiljan, Idvor, Smiljan]
        List placesOfBirth = (List) parser.parseExpression("Members.![placeOfBirth?.city]")
            .getValue(context);
        System.out.println(placesOfBirth);

        // You can also use a map to drive projection and, in this case,
        // the projection expression is evaluated against each entry in the map (represented as a Java Map.Entry).
        // The result of a projection across a map is a list that consists of the evaluation of
        // the projection expression against each map entry.
        Map<String, Inventor> map = new HashMap<>(3);
        map.put(inventor.getName(), inventor);
        map.put(inventor1.getName(), inventor1);
        map.put(inventor2.getName(), inventor2);
        context.setRootObject(map);

        // [China, Smiljan, China]
        List placesOfBirthOfCountry = parser.parseExpression("#root.![(key == 'AAA' or key == 'BBB')  ? " +
            "value.placeOfBirth?.country : value.placeOfBirth?.city]")
            .getValue(context, List.class);
        System.out.println(placesOfBirthOfCountry);
    }
}
